Home

Windows Fundamentals

 

Introduction to Windows

 

Overview

Microsoft Windows is an operating system that helps a person interact with a personal computer (PC). Programmers who want to create applications that people can use on MS Windows base them on a library called Win32.

Win32 is a library of data types, constants, functions, and classes (mainly structures) used to create applications for the Microsoft Windows operating system.

To create a basic application, you will first need a compiler that runs on a Microsoft Windows operating system. Although you can apply Win32 on various languages, including Pascal (namely Borland Delphi), we will use only one language. In reality the Win32 library is written in C, which is also the primary language of the Microsoft Windows operating systems. All of our programs will be written in C++. Normally you will not see a difference between C and C++ in our programs. Therefore, although all of the structures of Win32 are mostly C objects, we will use Win32 as if it were a C++ library. This simply means that, whenever needed, we will apply C++ rules.

Creating a Win32 Program

Normally, all Win32 programs look the same and behave the same but, just like C++ programs, there are small differences in terms of creating a program, depending on the compiler you are using. For my part, I will be testing our programs on Borland C++ Builder, Microsoft Visual C++ 6, Dev-C++, and Microsoft Visual C++ .Net.

For a basic Win32 program, the contents of a Win32 program is the same. You will feel a difference only when you start adding some objects called resources. 

Using Borland C++ Builder

To create a Win32 program using Borland C++ Builder, you must create a console application using the Console Wizard. You must make sure you don't select any option from the Console Wizard dialog box. After clicking OK, you are presented with a semi-empty file that contains only the inclusion of the windows.h library and the WinMain() function declaration. From there, you are ready.

From most environments used, Borland C++ builder is the only one that provides the easiest, but also unfortunately the emptiest template to create a Win32 application. It doesn't provide any real template nor any help on what to do with the provided file. In defense of the Borland C++ Builder, as you will see with Microsoft Visual C++ and Dev-C++, the other environments may fill your file with statements you don't need, you don't like, or you don't want. Therefore, Borland C++ Builder provides this empty file so you can freely decide how you want to create you program and what you want to include in your program. This means that I agree with Borland C++ Builder providing you with an empty file because at least the syntax of the WinMain() function is provided to you.

 

Practical Learning Practical Learning: Introducing Windows Programming

 
  1. Start Borland C++ Builder (5 or 6 I don't care and it doesn't matter).
  2. On the main menu, click File -> New... or File -> New -> Other...
     
    In numerous documentations, including Borland own, the New Items dialog box is also called the Object Repository. This provides the central point of application startup.
  3. In the New Items dialog box, click Console Wizard and click OK
  4. In the Console Wizard, make sure that only the C++ radio button is selected:
     
    The Console Wizard Dialog Box
     
  5. Click OK.
    You are presented with a file as follows:
     
    //---------------------------------------------------------------------------
    
    #include <windows.h>
    #pragma hdrstop
    
    //---------------------------------------------------------------------------
    
    #pragma argsused
    WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
    {
        return 0;
    }
    //---------------------------------------------------------------------------
  6. Save the application in a new folder named Win32A
  7. Save the first file as Main.cpp and save the project as SimpleWindow

Using Microsoft Visual C++

To create a Win32 application using Microsoft Visual C++, display the New (5 and 6 versions) or New Project (.Net version) dialog box and select Win32 application (5 and 6) or Win32 Project (.Net) item.

Microsoft Visual C++ provides the fastest and fairly most complete means of creating a Win32 application. For example it provides a skeleton application with all of the code a basic application would need. Because we are learning Win32, we will go the hard way, which consists of creating an application from scratch. If fact, this allows me to give almost (but not exactly) the same instructions as Borland C++ Builder.

  1. Start Start the Microsoft Development Environment.
  2. On the main menu, click File -> New... or File -> New -> Project...
  3. In the New or New Project dialog box, click either Win32 Application or click Visual C++ Projects and Win32 Project:
     
  4. In the location, type the path where the application should be stored, such as C:\Programs\MSVC
  5. In the Name edit box, type the name of the application as Win32A and click OK
  6. In the next dialog box of the wizard, if you are using MSVC 5 or 6, click the An Empty Project radio button:
     
    If you are using MSVC .Net, click Application Settings, then click the Console Application radio button, then click the Empty Project check box:
     
  7. Click Finish. If you are using MSVC 6, you will be presented with another dialog box; in this case click OK
  8. To create the first needed file of the program, if you are using MSVC 5 or 6, on the main menu, click File -> New. If you are using MSVC .Net, on the main menu, click Project -> Add New Item... 
  9. If you are using MSVC .Net, make sure that Visual C++ is selected in the Categories tree view.
    In both cases click either C++ Source File or C++ File (.cpp)
     
  10. In the Name edit box, replace the contents with a name for a file. In the case, replace it with Main and press Enter

About Microsoft Windows Messages

A computer application is equipped with Windows controls that allow the user to interact with the computer. Each control creates messages and sends them to the operating system. To manage these messages, they are handled by a function pointer called a Windows procedure. This function can appear as follows:

LRESULT CALLBACK MessageProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

This function uses a switch control to list all necessary messages and process each one in turn. This processes only the messages that you ask it to. If you have left-over messages, and you will always have un-processed messages, you can call the DefWindowProc() function at the end to take over.

The most basic message you can process is to make sure a user can close a window after using it. This can be done with a function called PostQuitMessage(). Its syntax is:

VOID PostQuitMessage(int nExitCode)

This function takes one argument which is the value of the LPARAM argument. To close a window, you can pass the argument as WM_QUIT.

Based on this, a simple Windows procedure can be defined as follows:

LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
    case WM_DESTROY:
        PostQuitMessage(WM_QUIT);
        break;
    default:
        return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    return 0;
}
 

Application Creation 

 

The Main Window Class

There are two primary things you must do in order to create even the simplest window: you must create the central point of the program, and you must tell the operating system how to respond when the user does what.

Just like a C++ program always has a main() function, a Win32 program needs a central function call WinMain. The syntax of that function is:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow );

Unlike the C++ main() function, the arguments of the WinMain() function are not optional. Your program will need them to communicate with the operating system.

The first argument, hInstance, is a handle to the instance of the program you are writing.

The second argument, hPrevInstance, is used if your program had any previous instance. If not, this argument can be ignored, which will always be the case.

The third argument, lpCmdLine, is a string that represents all items used on the command line to compile the application.

The last argument, nCmdShow, controls how the window you are building will be displayed.

An object that displays on your screen is called a window. Because there can be various types of windows in your programs, your first responsibility is to control them, know where they are, what they are doing, why, and when. The first control you must exercise on these different windows is to host them so that all windows of your program belong to an entity called the main window. This main window is created using an object that can be called a class (strictly, a structure).

The Win32 library provides two classes for creating the main window and you can use any one of them. They are WNDCLASS and WNDCLASSEX. The second adds only a slight feature to the first. Therefore, we will mostly use the WNDCLASSEX structure for our lessons.

The WNDCLASS and the WNDCLASSEX classes are defined as follows:

 typedef struct _WNDCLASS { 
    UINT       style; 
    WNDPROC    lpfnWndProc; 
    int        cbClsExtra; 
    int        cbWndExtra; 
    HINSTANCE  hInstance; 
    HICON      hIcon; 
    HCURSOR    hCursor; 
    HBRUSH     hbrBackground; 
    LPCTSTR    lpszMenuName; 
    LPCTSTR    lpszClassName; 
} WNDCLASS, *PW
typedef struct _WNDCLASSEX { 
    UINT       cbSize; 
    UINT       style; 
    WNDPROC    lpfnWndProc; 
    int        cbClsExtra; 
    int        cbWndExtra; 
    HINSTANCE  hInstance; 
    HICON      hIcon; 
    HCURSOR    hCursor; 
    HBRUSH     hbrBackground; 
    LPCTSTR    lpszMenuName; 
    LPCTSTR    lpszClassName; 
    HICON      hIconSm; 
} WNDCLASSEX, *PWNDCLASSEX;

The Size of the Window Class

To create a window, you must "fill out" this class, which means you must provide a value for each of its members so the operating system would know what your program is expected to do.

The first thing you must do in order to create an application is to declare a variable of either WNDCLASS or WNDCLASSEX type.

After declaring a WNDCLASSEX variable, you must specify its size. This is done by  initializing your variable with the sizeof operator applied to the window class as follows:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize = sizeof(WNDCLASSEX);

    return 0;
}

Additional Memory Request

Upon declaring a WNDCLASSEX variable, the compiler allocates an amount of memory space for it, as it does for all other variables. If you think you will need more memory than allocated, assign the number of extra bytes to the cbClsExtra member variable. Otherwise, the compiler initializes this variable to 0. If you do not need extra memory for your WNDCLASSEX variable, initialize this member with 0. Otherwise, you can do it as follows:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize     = sizeof(WNDCLASSEX);
	WndClsEx.cbClsExtra = 0;

	return 0;
}

 

The Application's Instance

Creating an application is equivalent to creating an instance for it. To communicate to the WinMain() function that you want to create an instance for your application, which is, to make it available as a resource, assign the WinMain()'s hInstance argument to your WNDCLASS variable:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize     = sizeof(WNDCLASSEX);
	WndClsEx.cbClsExtra = 0;
	WndClsEx.hInstance  = hInstance;

	return 0;
}

Window Extra-Memory

When an application has been launched and is displaying on the screen, which means an instance of the application has been created, the operating system allocates an amount of memory space for that application to use. If you think that your application's instance will need more memory than that, you can request that extra memory bytes be allocated to it. Otherwise, you can let the operating system handle this instance memory issue and initialize the cbWndExtra member variable to 0:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize     = sizeof(WNDCLASSEX);
	WndClsEx.cbClsExtra = 0;
	WndClsEx.cbWndExtra = 0;
	WndClsEx.hInstance  = hInstance;

	return 0;
}

The Main Window's Style

The style member variable specifies the primary operations applied on the window class. The actual available styles are constant values. For example, if a user moves a window or changes its size, you would need the window to be redrawn to get its previous characteristics. To redraw the window horizontally, you would apply the CS_HREDRAW. In the same way, to redraw the window vertically, you can apply the CS_VREDRAW.

The styles are combined using the bitwise OR (|) operator. The CS_HREDRAW and the CS_VREDRAW styles can be combined and assigned to the style member variable as follows:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize     = sizeof(WNDCLASSEX);
	WndClsEx.style      = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.cbClsExtra = 0;
	WndClsEx.cbWndExtra = 0;
	WndClsEx.hInstance  = hInstance;

	return 0;
}

Message Processing

The name of the window procedure we reviewed earlier must be assigned to the lpfnWndProc member variable of the WNDCLASS or WNDCLASSEX variable. This can be defined as follows:

#include <windows.h>

LRESULT WndProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize      = sizeof(WNDCLASSEX);
	WndClsEx.style       = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc = WndProcedure;
	WndClsEx.cbClsExtra  = 0;
	WndClsEx.cbWndExtra  = 0;
	WndClsEx.hInstance   = hInstance;

	return 0;
}

LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
    case WM_DESTROY:
        PostQuitMessage(WM_QUIT);
        break;
    default:
        return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    return 0;
}

The Application Main Icon

An icon can be used to represent an application in My Computer or Windows Explorer. To assign this small picture to your application, you can either use an existing icon or design your own. To make your programming a little faster, Microsoft Windows installs a few icons. The icon is assigned to the hIcon member variable using the LoadIcon() function. For a Win32 application, the syntax of this function is:

HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);

The hInstance argument is a handle to the file in which the icon was created. This file is usually stored in a library (DLL) of an executable program. If the icon was created as part of your application, you can use the hInstance of your application. If your are using one of the icons below, set this argument to NULL.

The lpIconName is the name of the icon to be loaded. This name is added to the resource file when you create the icon resource. It is added automatically if you add the icon as part of your resources; otherwise you can add it manually when creating your resource script. Normally, if you had created and designed an icon and gave it an identifier, you can pass it using the MAKEINTRESOURCE macro.

To make your programming a little faster, Microsoft Windows installs a few icons you can use for your application. These icons have identification names that you can pass to the LoadIcon() function as the lpIconName argument. The icons are:

ID Picture
IDI_APPLICATION Application Icon
IDI_INFORMATION Information
IDI_ASTERISK Asterisk
IDI_QUESTION Question
IDI_WARNING Warning
IDI_EXCLAMATION Exclamation
IDI_HAND Hand
IDI_ERROR Error

If you designed your own icon (you should make sure you design a 32x32 and a 16x16 versions, even for convenience), to use it, specify the hInstance argument of the LoadIcon() function to the instance of your application. Then use the MAKEINTRESOURCE macro to convert its identifier to a null-terminated string. This can be done as follows:

WndCls.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_STAPLE));

The icon can be specified by its name, which would be a null-terminated string passed as lpszResourceName. If you had designed your icon and gave it an ID, you can pass this identifier to the LoadIcon() method.

The LoadIcon() member function returns an HICON object that you can assign to the hIcon member variable of your WNDCLASS object. Besides the regular (32x32) icon, the WNDCLASSEX structure allows you to specify a small icon (16x16) to use in some circumstances. You can specify both icons as follows:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize      = sizeof(WNDCLASSEX);
	WndClsEx.style       = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc = WndProcedure;
	WndClsEx.cbClsExtra  = 0;
	WndClsEx.cbWndExtra  = 0;
	WndClsEx.hIcon       = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hInstance   = hInstance;
	WndClsEx.hIconSm     = LoadIcon(NULL, IDI_APPLICATION);

	return 0;
}

Introduction to Cursors

If you designed your own icon (you should make sure you design a 32x32 and a 16x16 versions, even for convenience), to use it, specify the hInstance argument of the LoadIcon() function to the instance of your application. Then use the MAKEINTRESOURCE macro to convert its identifier to a null-terminated string. This can be done as follows:

WndCls.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_STAPLE));

The icon can be specified by its name, which would be a null-terminated string passed as lpszResourceName. If you had designed your icon and gave it an ID, you can pass this identifier to the LoadIcon() method.

The LoadIcon() member function returns an HICON object that you can assign to the hIcon member variable of your WNDCLASS object. Besides the regular (32x32) icon, the WNDCLASSEX structure allows you to specify a small icon (16x16) to use in some circumstances. You can specify both icons as follows:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize      = sizeof(WNDCLASSEX);
	WndClsEx.style       = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc = WndProcedure;
	WndClsEx.cbClsExtra  = 0;
	WndClsEx.cbWndExtra  = 0;
	WndClsEx.hIcon       = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hInstance   = hInstance;
	WndClsEx.hIconSm     = LoadIcon(NULL, IDI_APPLICATION);

	return 0;
}

A cursor is used to locate the position of the mouse pointer on a document or the screen. To use a cursor, call the Win32 LoadCursor() function. Its syntax is:

HCURSOR LoadCursor(HINSTANCE hInstance, LPCTSTR lpCursorName);

The hInstance argument is a handle to the file in which the cursor was created. This file is usually stored in a library (DLL) of an executable program. If the cursor was created as part of your application, you can use the hInstance of your application. If your are using one of the below cursors, set this argument to NULL.

When Microsoft Windows installs, it also installs various standard cursors you can use in your program. Each one of these cursors is recognized by an ID which is simply a constant integers. The available cursors are:

ID Picture Description
IDC_APPSTARTING App Start Used to show that something undetermined is going on or the application is not stable
IDC_ARROW Arrow This standard arrow is the most commonly used cursor
IDC_CROSS The crosshair cursor is used in various circumstances such as drawing
IDC_HAND The Hand is standard only in Windows 2000. If you are using a previous operating system and need this cursor, you may have to create your own.
IDC_HELP The combined arrow and question mark cursor is used when providing help on a specific item on a window object
IDC_IBEAM The I-beam cursor is used on text-based object to show the position of the caret
IDC_ICON This cursor is not used anymore
IDC_NO This cursor can be used to indicate an unstable situation
IDC_SIZE This cursor is not used anymore
IDC_SIZEALL The four arrow cursor pointing north, south, east, and west is highly used to indicate that an object is selected or that it is ready to be moved
IDC_SIZENESW The northeast and southwest arrow cursor can be used when resizing an object on both the length and the height
IDC_SIZENS The  north - south arrow pointing cursor can be used when shrinking or heightening an object
IDC_SIZENWSE The northwest - southeast arrow pointing cursor can be used when resizing an object on both the length and the height
IDC_SIZEWE The west - east arrow pointing cursor can be used when narrowing or enlarging an object
IDC_UPARROW The vertical arrow cursor can be used to indicate the presence of the mouse or the caret
IDC_WAIT The Hourglass cursor is usually used to indicate that a window or the application is not ready.

The LoadCursor() member function returns an HCURSOR value. You can assign it to the hCursor member variable of your WNDCLASS object. Here is an example:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize      = sizeof(WNDCLASSEX);
	WndClsEx.style       = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc = WndProcedure;
	WndClsEx.cbClsExtra  = 0;
	WndClsEx.cbWndExtra  = 0;
	WndClsEx.hIcon       = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hCursor     = LoadCursor(NULL, IDC_ARROW);
	WndClsEx.hInstance   = hInstance;
	WndClsEx.hIconSm     = LoadIcon(NULL, IDI_APPLICATION);

	return 0;
}

 

The Window's Background Color

To paint the work area of the window, you must specify what color will be used to fill it. This color is created as an HBRUSH and assigned to the hbrBackground member variable of your WNDCLASS or WNDCLASSEX variable. The color you are using must be a valid HBRUSH or you can cast a known color to HBRUSH. The Win32 library defines a series of colors known as stock objects. To use one of these colors, call the GetStockObject() function. For example, to paint the windows background in black, you can pass the BLACK_BRUSH constant to the GetStockObject() function, cast it to HBRUSH and assign the result to hbrBackground.

In addition to the stock objects, the Microsoft Windows provides a series of colors for its own internal use. These are the colors used to paint the borders of frames, buttons, scroll bars, title bars, text, etc. The colors are named (you should be able to predict their appearance or role from their name) COLOR_ACTIVEBORDER, COLOR_ACTIVECAPTION, COLOR_APPWORKSPACE, COLOR_BACKGROUND, COLOR_BTNFACE, COLOR_BTNSHADOW, COLOR_BTNTEXT, COLOR_CAPTIONTEXT, COLOR_GRAYTEXT, COLOR_HIGHLIGHT, COLOR_HIGHLIGHTTEXT, COLOR_INACTIVEBORDER, COLOR_INACTIVECAPTION, COLOR_MENU, COLOR_MENUTEXT, COLOR_SCROLLBAR, COLOR_WINDOW, COLOR_WINDOWFRAME, and COLOR_WINDOWTEXT. You can use any of these colors to paint the background of your window. First cast it to HBRUSH and assign it to hbrBackground:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize        = sizeof(WNDCLASSEX);
	WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc   = WndProcedure;
	WndClsEx.cbClsExtra    = 0;
	WndClsEx.cbWndExtra    = 0;
	WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
	WndClsEx.hbrBackground = GetStockObject(WHITE_BRUSH);
	WndClsEx.hInstance     = hInstance;
	WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

	return 0;
}

 

The Application's Main Menu

If you want the window to display a menu, first create or design the resource menu (we will eventually learn how to do this). After creating the menu, assign its name to the lpszMenuName name to your WNDCLASS or WNDCLASSEX variable. Otherwise, pass this argument as NULL. Here is an example:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize        = sizeof(WNDCLASSEX);
	WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc   = WndProcedure;
	WndClsEx.cbClsExtra    = 0;
	WndClsEx.cbWndExtra    = 0;
	WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
	WndClsEx.hbrBackground = GetStockObject(WHITE_BRUSH);
	WndClsEx.lpszMenuName  = NULL;
	WndClsEx.hInstance     = hInstance;
	WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

	return 0;
}

The Window's Class Name

To create a window, you must provide its name as everything else in the computer has a name. The class name of your main window must be provided to the lpszClassName member variable of your WNDCLASS or WNDCLASSEX variable. You can provide the name to the variable or declare a global null-terminated string. Here is an example:

const char *ClsName = "BasicApp";

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	WndClsEx.cbSize        = sizeof(WNDCLASSEX);
	WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc   = WndProcedure;
	WndClsEx.cbClsExtra    = 0;
	WndClsEx.cbWndExtra    = 0;
	WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
	WndClsEx.hbrBackground = GetStockObject(WHITE_BRUSH);
	WndClsEx.lpszMenuName  = NULL;
	WndClsEx.lpszClassName = ClsName;
	WndClsEx.hInstance     = hInstance;
	WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

	return 0;
}
 

Finalizing an Application

 

Window Registration

After initializing the window class, you must make it available to the other controls that will be part of your application. This process is referred to as registration. To register the window class, call the RegisterClass() for a WNDCLASS variable. If you created your window class using the WNDCLASSEX structure, call the RegisterClassEx() function. Their syntaxes are:

ATOM RegisterClass(CONST WNDCLASS *lpWndClass);
ATOM RegisterClassEx(CONST WNDCLASSEX *lpwcx);

The function simply takes as argument a pointer to a WNDCLASS or WNDCLASSEX. This call can be done as follows:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX WndClsEx;

	. . .

	RegisterClassEx(&WndClsEx);

	return 0;
}
 

Window Creation

The WNDLCLASS and the WNDCLASSEX classes are used to initialize the application window class. To display a window, that is, to give the user an object to work with, you must create a window object. This window is the object the user uses to interact with the computer.

To create a window, you can call either the CreateWindow() or the CreateWindowEx() function. We will come back to these functions.

You can simply call this function and specify its arguments after you have registered the window class. Here is an example:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX	WndCls;

	. . .

    	RegisterClassEx(&WndClsEx);

    	CreateWindow(. . .);
}

If you are planning to use the window further in your application, you should retrieve the result of the CreateWindow() or the CreateWindowEx() function, which is a handle to the window that is being created. To do this, you can declare an HWND variable and initialize it with the create function. This can be done as follows:

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine, int nCmdShow)
{
	HWND	hWnd;
	WNDCLASSEX	WndClsEx;

	. . .

    	RegisterClassEx(&WndClsEx);

    	hWnd = CreateWindow(. . .);
}

We will come back to the other characteristics of a window in future lessons.

 

 

Window's Message Decoding

Once a window has been created, the user can use it. This is done by the user clicking things with the mouse or pressing keys on the keyboard. A message that a window sends is received by the application. This application must analyze, translate, and decode the message to know what object sent the message and what the message consists of. To do this, the application uses the GetMessage() function. Its syntax is:

BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);

The lpMsg argument is a pointer to the MSG structure. The MSG structure is defined as follows:

typedef struct tagMSG {
    HWND   hwnd; 
    UINT   message; 
    WPARAM wParam; 
    LPARAM lParam; 
    DWORD  time; 
    POINT  pt; 
} MSG, *PMSG;

The hWnd argument identifies which window sent the message. If you want the messages of all windows to be processed, pass this argument as NULL.

The wMsgFilterMin and the wMsgFilterMax arguments specify the message values that will be treated. If you want all messages to be considered, pass each of them as 0.

Once a message has been sent, the application analyzes it using the TranslateMessage() function. Its syntax is:

BOOL TranslateMessage(CONST MSG *lpMsg);

This function takes as argument the MSG object that was passed to the GetMessage() function and analyzes it. If this function successfully translates the message, it returns TRUE. If it cannot identify and translate the message, it returns FALSE.

Once a message has been decoded, the application must send it to the window procedure. This is done using the DispatchMessage() function. Its syntax is:

LRESULT DispatchMessage(CONST MSG *lpMsg);

This function also takes as argument the MSG object that was passed to GetMessage() and analyzed by TranslateMessage(). This DispatchMessage() function sends the lpMsg message to the window procedure. The window procedure processes it and sends back the result, which becomes the return value of this function. Normally, when the window procedure receives the message, it establishes a relationship with the control that sent the message and starts treating it. By the time the window procedure finishes with the message, the issue is resolved (or aborted). This means that, by the time the window procedure returns its result, the message is not an issue anymore. For this reason you will usually, if ever, not need to retrieve the result of the DispatchMessage() function.

This translating and dispatching of messages is an on-going process that goes on as long as your application is running and as long as somebody is using it. For this reason, the application uses a while loop to continuously check new messages. This behavior can be implemented as follows:

while( GetMessage(&Msg, NULL, 0, 0) )
{
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
}

If the WinMain() function successfully creates the application and the window, it returns the wParam value of the MSG used on the application.

 

Practical Learning Practical Learning: Creating a Sample Application

  1. Replace the file with the following (the file in Borland C++ Builder contains some lines with #pragma; you don't need to delete these files because their presence or absence will not have  a negative impact on the compilation of the program):
     
    #include <windows.h>
    
    const char *ClsName = "BasicApp";
    const char *WndName = "A Simple Window";
    
    LRESULT CALLBACK WndProcedure(HWND hWnd, UINT uMsg,
    			   WPARAM wParam, LPARAM lParam);
    
    INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
    {
    	MSG        Msg;
    	HWND       hWnd;
    	WNDCLASSEX WndClsEx;
    
    	// Create the application window
    	WndClsEx.cbSize        = sizeof(WNDCLASSEX);
    	WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
    	WndClsEx.lpfnWndProc   = WndProcedure;
    	WndClsEx.cbClsExtra    = 0;
    	WndClsEx.cbWndExtra    = 0;
    	WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    	WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
    	WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    	WndClsEx.lpszMenuName  = NULL;
    	WndClsEx.lpszClassName = ClsName;
    	WndClsEx.hInstance     = hInstance;
    	WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
    
    	// Register the application
    	RegisterClassEx(&WndClsEx);
    
    	// Create the window object
    	hWnd = CreateWindow(ClsName,
    			  WndName,
    			  WS_OVERLAPPEDWINDOW,
    			  CW_USEDEFAULT,
    			  CW_USEDEFAULT,
    			  CW_USEDEFAULT,
    			  CW_USEDEFAULT,
    			  NULL,
    			  NULL,
    			  hInstance,
    			  NULL);
    	
    	// Find out if the window was created
    	if( !hWnd ) // If the window was not created,
    		return 0; // stop the application
    
    	// Display the window to the user
    	ShowWindow(hWnd, SW_SHOWNORMAL);
    	UpdateWindow(hWnd);
    
    	// Decode and treat the messages
    	// as long as the application is running
    	while( GetMessage(&Msg, NULL, 0, 0) )
    	{
                 TranslateMessage(&Msg);
                 DispatchMessage(&Msg);
    	}
    
    	return Msg.wParam;
    }
    
    LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg,
    			   WPARAM wParam, LPARAM lParam)
    {
        switch(Msg)
        {
        // If the user wants to close the application
        case WM_DESTROY:
            // then close it
            PostQuitMessage(WM_QUIT);
            break;
        default:
            // Process the left-over messages
            return DefWindowProc(hWnd, Msg, wParam, lParam);
        }
        // If something was not done, let it go
        return 0;
    }
  2. To execute the program, if you are using Borland C++ Builder, press F9
    If you are using Microsoft Visual C++, press Ctrl + F5 and click Yes
     
    A Simple Window
  3. To close the window, click its system Close button and return to your programming environment.
 

Home Copyright © 2003-2004 FunctionX, Inc. Next